package com.linln.admin.pcs.service;

import com.alibaba.fastjson.JSONObject;
import com.linln.admin.pcs.api.WxJsPayController;
import com.linln.admin.util.WXPayUtil;
import com.linln.common.data.QuerySpec;
import com.linln.common.utils.SnowflakeIdGenerator;
import com.linln.modules.system.domain.CompanyParam;
import com.linln.modules.system.domain.Order;
import com.linln.modules.system.domain.PayParam;
import com.linln.modules.system.domain.PayRecord;
import com.linln.modules.system.repository.CompanyParamRepository;
import com.linln.modules.system.repository.CompanyRepository;
import com.linln.modules.system.repository.OrderRepository;
import com.linln.modules.system.repository.PayRecordRepository;
import com.thoughtworks.xstream.XStream;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.*;


@Service
public class WxJsPayService {

    private final static Logger logger = LoggerFactory.getLogger(WxJsPayService.class);

    // 统一下单接口URL
    private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    private static final String ORDER_QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";

    private static final String REFUND_QUERY_URL = "https://api.mch.weixin.qq.com/pay/refundquery";

    @Value("${wx.appId}")
    private String APPID;

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private CompanyRepository companyRepository;

    @Autowired
    private CompanyParamRepository companyParamRepository;

    @Autowired
    private PayRecordRepository payRecordRepository;


    // 生成预支付订单
    public Map<String, Object> createOrder(PayParam payParam) throws Exception {
        String outTradeNo = payParam.getOutTradeNo();
        //子订单号
        String orderChildNo = payParam.getOrderChildNo();
        Map<String, String> params = new HashMap<>();
        params.put("appid", payParam.getAppId()); // 公众号ID
        params.put("mch_id", payParam.getMchId()); // 商户号
        params.put("nonce_str", generateNonceStr()); // 随机字符串
        params.put("body", payParam.getBody()); // 商品描述
        params.put("out_trade_no", orderChildNo); // 商户订单号
        params.put("total_fee", payParam.getTotalFee()); // 总金额（分）
        params.put("spbill_create_ip", getLocalIp()); // 客户端IP
        params.put("notify_url", payParam.getNotifyUrl()); // 回调地址
        params.put("trade_type", "JSAPI"); // 交易类型
        params.put("openid", payParam.getOpenId()); // 用户标识

        // 生成签名
        params.put("sign", WXPayUtil.generateSign(params, payParam.getApiKey()));
        logger.error("预支付订单参数: "+params);
        // 转换为XML并发送请求
        String xml = toXml(params);
        String responseXml = post(UNIFIED_ORDER_URL, xml);

        PayRecord payRecord = new PayRecord();
        payRecord.setCompanyId(payParam.getCompanyId());
        payRecord.setOrderNo(outTradeNo);
        payRecord.setOrderChildNo(orderChildNo);
        payRecord.setPatientName(payParam.getPatientName());
        payRecord.setPayFee(payParam.getTotalFee());

        // 解析返回结果
        Map<String, String> result = parseXml(responseXml);
        payRecord.setOperatorStatus(1);
        payRecord.setResult(responseXml);

        Map<String, Object> orderMap = new HashMap<>();
        if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS".equals(result.get("result_code"))) {
            payRecord.setMsg("支付成功");
            payRecord.setPayStatus(1);
            payRecord.setPayBackStatus(2);
            payRecordRepository.save(payRecord);
            orderMap.put("code","1");
            orderMap.put("msg","支付成功");
            orderMap.put("data",result);
        } else {
            payRecord.setMsg("统一下单失败：" + result.get("err_code_des"));
            payRecord.setPayBackStatus(0);
            payRecord.setPayStatus(0);
            payRecordRepository.save(payRecord);
            orderMap.put("code","0");
            orderMap.put("msg","统一下单失败");
            orderMap.put("data",result);
        }
        return orderMap;
    }


    /**
     * 查询支付订单
     * @param payParam
     * @return
     * @throws Exception
     */
    public Map<String, String> queryOrder(PayParam payParam) throws Exception {
        String outTradeNo = payParam.getOutTradeNo();
        logger.error("子订单号:"+outTradeNo);
        String mchId = payParam.getMchId();
        // 1. 构建参数
        Map<String, String> params = new HashMap<>();
        params.put("appid", APPID);
        params.put("mch_id", mchId);
        params.put("out_trade_no", outTradeNo);
        params.put("nonce_str", generateNonceStr());
        params.put("sign", WXPayUtil.generateSign(params, payParam.getApiKey()));

        // 2. 构造XML
        String xml = toXml(params);

        ClassPathResource resource = new ClassPathResource(payParam.getKeyPath());
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(resource.getInputStream(), mchId.toCharArray());

        SSLContext sslContext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, mchId.toCharArray())
                .build();

        // 3. 创建SSL HttpClient
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLContext(sslContext)
                .build();

        // 4. 发送请求
        HttpPost httpPost = new HttpPost(ORDER_QUERY_URL);
        httpPost.setEntity(new StringEntity(xml, "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        Map<String, String> map = new HashMap<>();
        if (null == response){
            map.put("code","400");
            map.put("msg","查询订单失败：响应为空");
        }

        // 5. 处理响应
        String responseXml = EntityUtils.toString(response.getEntity(), "UTF-8");

        if (null == responseXml || "".equals(responseXml)){
            map.put("code","400");
            map.put("msg","查询订单失败：响应为空");
        }

        Map<String, String> resultMap = parseXml(responseXml);
        logger.error("查询订单:"+outTradeNo+" 返回结果:"+resultMap);
        if (resultMap.get("return_code").equals("SUCCESS") && resultMap.get("result_code").equals("SUCCESS")) {
            String tradeState = resultMap.get("trade_state");
            if (tradeState.equals("SUCCESS")) {
                map.put("code","200");
                map.put("msg","订单支付成功");
            }else {
                map.put("code","500");
                map.put("msg","支付失败，状态：" + tradeState + ", 描述：" + resultMap.get("trade_state_desc"));
            }
        } else {
            logger.error("查询订单失败:"+outTradeNo+" 返回结果:"+resultMap);
            map.put("code","400");
            map.put("msg","查询订单失败：" + resultMap.get("err_code_des"));
        }
        return map;
    }


    /**
     * 查询退款订单
     * @param payParam
     * @return
     * @throws Exception
     */
    public Map<String, String> queryRefundOrder(PayParam payParam) throws Exception {
        String outTradeNo = payParam.getOutTradeNo();
        String outRefundNo = payParam.getOutRefundNo();
        logger.error("查询退款订单:" + outRefundNo);
        // 构造请求参数
        String mchId = payParam.getMchId();
        Map<String, String> params = new HashMap<>();
        params.put("appid", APPID); // 应用ID
        params.put("mch_id", payParam.getMchId()); // 商户号
        params.put("out_refund_no", outRefundNo); // 商户退款单号
        params.put("out_trade_no", outTradeNo); // 商户退款单号
        params.put("nonce_str", generateNonceStr()); // 随机字符串
        params.put("sign", WXPayUtil.generateSign(params, payParam.getApiKey())); // 签名

        ClassPathResource resource = new ClassPathResource(payParam.getKeyPath());
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(resource.getInputStream(), mchId.toCharArray());
        // 将参数转换为XML格式
        String xmlRequest = toXml(params);
        SSLContext sslContext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, mchId.toCharArray())
                .build();

        // 3. 创建SSL HttpClient
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLContext(sslContext)
                .build();
        HttpPost httpPost = new HttpPost(REFUND_QUERY_URL);
        httpPost.setEntity(new StringEntity(xmlRequest, "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        // 获取响应内容
        String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
        // 解析XML响应
        Map<String, String> result = parseXml(responseBody);
        logger.error("解析XML响应:" + result);
        Map<String, String> refundResult = new HashMap<>();
        // 判断退款结果
        if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS".equals(result.get("result_code"))) {
            String refundStatus = result.getOrDefault("refund_status_0", "UNKNOWN");
            if ("SUCCESS".equals(refundStatus)) {
                refundResult.put("code","200");
                refundResult.put("msg","退款成功");
            } else if ("PROCESSING".equals(refundStatus)) {
                refundResult.put("code","300");
                refundResult.put("msg","退款处理中");
            } else {
                refundResult.put("code","400");
                refundResult.put("msg","退款失败:"+result);
            }
        } else {
            refundResult.put("code","500");
            refundResult.put("msg","查询失败，错误信息:"+result);
        }
        return refundResult;
    }


    private boolean verifySign(Map<String, String> params, String apiKey) throws Exception {
        String sign = params.remove("sign");
        String generatedSign = WXPayUtil.generateSign(params, apiKey);
        return generatedSign.equals(sign);
    }

    // 生成随机字符串
    private String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

    // 获取本地IP
    private String getLocalIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            return "127.0.0.1";
        }
    }

    // 发送POST请求
    private String post(String url, String xml) throws Exception {
        URL u = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) u.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.getOutputStream().write(xml.getBytes("UTF-8"));

        StringBuilder sb = new StringBuilder();
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        return sb.toString();
    }

    // 转换Map为XML
    private String toXml(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        sb.append("<xml>");
        for (Map.Entry<String, String> entry : params.entrySet()) {
            sb.append("<").append(entry.getKey()).append(">").append(entry.getValue()).append("</").append(entry.getKey()).append(">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    // 解析XML为Map
    private Map<String, String> parseXml(String xml) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(new StringReader(xml));
        Element root = document.getRootElement();
        Map<String, String> map = new HashMap<>();
        for (Iterator<Element> it = root.elementIterator(); it.hasNext(); ) {
            Element e = it.next();
            map.put(e.getName(), e.getText());
        }
        return map;
    }
}
